TOOL.Category		= "Construction"
TOOL.Name			= "Dynamic Weight"
TOOL.Command		= nil
TOOL.ConfigName		= ""

TOOL.ClientConVar["key_increase"] = "8"
TOOL.ClientConVar["key_decrease"] = "2"
TOOL.ClientConVar["model"] = "models/props_interiors/pot01a.mdl"
TOOL.ClientConVar["max_weight"] = "1000"
TOOL.ClientConVar["min_weight"] = "500"
TOOL.ClientConVar["default_weight"] = "750"
TOOL.ClientConVar["change_rate"] = "250"
TOOL.ClientConVar["auto_return"] = "0"
TOOL.ClientConVar["toggle"] = "0"
TOOL.ClientConVar["collide_objects"] = "0"
TOOL.ClientConVar["collide_world"] = "1"

cleanup.Register ("dynamic_weights")

if CLIENT then 
 	language.Add("Tool_dynamic_weight_name", "Dynamic Weight")
	language.Add("Tool_dynamic_weight_desc", "Numpad-controlled weight.")
	language.Add("Tool_dynamic_weight_0", "Left-click anywhere to spawn a weight. Right-click existing weights to change them.")
	
	language.Add("Undone_Dynamic Weight", "Undone Dynamic Weight")
	
	language.Add("Cleanup_dynamic_weights", "Dynamic Weights")
	language.Add("Cleaned_dynamic_weights", "Cleaned up all Dynamic Weights")
	language.Add("SBoxLimit_dynamic_weights", "You've reached the Dynamic Weight limit!")
end

function TOOL:LeftClick (trace)
	if trace.Entity and trace.Entity:IsPlayer() then return false end 
	
 	if SERVER and not util.IsValidPhysicsObject (trace.Entity, trace.PhysicsBone) then return false end
 	if CLIENT then return true end
	
	local ply = self:GetOwner()
	
	local key_increase = self:GetClientNumber ("key_increase")
	local key_decrease = self:GetClientNumber ("key_decrease")
	local model = self:GetClientInfo ("model")
	local max_weight = self:GetClientNumber ("max_weight")
	local min_weight = self:GetClientNumber ("min_weight")
	local default_weight = math.min (math.max (self:GetClientNumber ("default_weight"), min_weight), max_weight)
	local change_rate = self:GetClientNumber ("change_rate")
	local auto_return = self:GetClientNumber ("auto_return")
	local toggle = self:GetClientNumber ("toggle")
	local collide_objects = self:GetClientNumber ("collide_objects")
	local collide_world = self:GetClientNumber ("collide_world")
	
	if not self:GetSWEP():CheckLimit("dynamic_weights") then return false end
	
	if not util.IsValidModel(model) then return false end
 	if not util.IsValidProp(model) then return false end
	
	local Ang = trace.HitNormal:Angle()
	Ang.pitch = Ang.pitch + 90
	
	local weight = MakeDynamicWeight (ply, model, Ang, trace.HitPos, key_increase, key_decrease, model, max_weight, min_weight, default_weight, change_rate, auto_return, toggle, collide_objects, collide_world)
	if not weight then return end
	
	local min = weight:OBBMins()
	weight:SetPos (trace.HitPos - trace.HitNormal * min.z)
	
	--weld/nocollide
	local const, nocollide
	
	if trace.Entity:IsValid() then
		const = constraint.Weld (weight, trace.Entity, 0, trace.PhysicsBone, 0, true, true)
	end
	
	undo.Create ("Dynamic Weight")
		undo.AddEntity (weight)
		undo.AddEntity (const)
		undo.SetPlayer (ply)
	undo.Finish ()
	
	ply:AddCleanup ("dynamic_weights", weight)
	ply:AddCleanup ("dynamic_weights", const)
	
	return true
end

function TOOL:RightClick (trace)
	if trace.Entity:IsValid() && trace.Entity:GetClass() == "gmod_dynamic_weight" && trace.Entity.pl == self:GetOwner() then
 		local mergetable = {
			max_weight = self:GetClientNumber ("max_weight"),
			min_weight = self:GetClientNumber ("min_weight"),
			default_weight = self:GetClientNumber ("default_weight"),
			change_rate = self:GetClientNumber ("change_rate"),
			auto_return = self:GetClientNumber ("auto_return"),
			toggle = self:GetClientNumber ("toggle")
		}
		
		table.Merge (trace.Entity:GetTable(), mergetable)
		trace.Entity:GetTable():Think (true)
 		return true
 	end
end

if SERVER then
	CreateConVar('sbox_maxdynamic_weights', 20)
	
	function MakeDynamicWeight (pl, Model, Ang, Pos, key_increase, key_decrease, model, max_weight, min_weight, default_weight, change_rate, auto_return, toggle, collide_objects, collide_world)
		if not pl:CheckLimit ("dynamic_weights") then return false end
		
		local weight = ents.Create ("gmod_dynamic_weight")
		if not weight:IsValid() then return end
		weight:SetModel (Model)
		
		weight:SetAngles (Ang)
		weight:SetPos (Pos)
		weight:Spawn ()
		
		weight:GetPhysicsObject():SetMass (default_weight)
		
		numpad.OnDown (pl, key_increase, "Weight_Press", weight, 1)
		numpad.OnUp (pl, key_increase, "Weight_Release", weight, 1)
		
		numpad.OnDown (pl, key_decrease, "Weight_Press", weight, -1)
		numpad.OnUp (pl, key_decrease, "Weight_Release", weight, -1)
		--value setting party time
		local mergetable = {
			key_increase = key_increase,
			key_decrease = key_decrease,
			max_weight = max_weight,
			min_weight = min_weight,
			default_weight = default_weight,
			change_rate = change_rate,
			auto_return = auto_return,
			toggle = toggle,
			collide_objects = collide_objects,
			collide_world = collide_world,
			pl = pl
		}
		
		table.Merge (weight:GetTable(), mergetable)
		weight:GetTable():SetPlayer (pl)
		weight:GetTable():Think (true)
		
		pl:AddCount ("dynamic_weights", weight)
		
		DoPropSpawnedEffect (weight)
		
		return weight
	end
	
	duplicator.RegisterEntityClass ("gmod_dynamic_weight", MakeDynamicWeight, "Model", "Ang", "Pos")
end

local models = {
	["Kettle"] = {dynamic_weight_model = "models/props_interiors/pot01a.mdl"},
	["Helicopter Mine"] = {dynamic_weight_model = "models/Combine_Helicopter/helicopter_bomb01.mdl"},
	["Briefcase"] = {dynamic_weight_model = "models/props_c17/BriefCase001a.mdl"},
	["Tabletop Globe"] = {dynamic_weight_model = "models/props_combine/breenglobe.mdl"}
}

function TOOL.BuildCPanel (cp)
	cp:AddControl("Header", {Text = "Dynamic Weight", Description = "Dynamic Weight"}) 
	cp:AddControl("Label", {Text = "Made by Devenger."})
	cp:AddControl("Label", {Text = "Report problems to devenger@gmail.com."})
	cp:AddControl("ComboBox", {Label = "Model", MenuButton = "0", Options = models})
	cp:AddControl("Numpad", {Label = "Increase Weight", Label2 = "Decrease Weight", Command = "dynamic_weight_key_increase", Command2 = "dynamic_weight_key_decrease", ButtonSize = 22})
	cp:AddControl("Slider",  {Label = "Default Weight", Type = "Float", Min = 1, Max = 5000, Command = "dynamic_weight_default_weight", Description = "The default, starting weight of the object."})
	cp:AddControl("Slider",  {Label = "Maximum Weight", Type = "Float", Min = 1, Max = 5000, Command = "dynamic_weight_max_weight", Description = "The maximum weight the object can extend to."})
	cp:AddControl("Slider",  {Label = "Minimum Weight", Type = "Float", Min = 1, Max = 5000, Command = "dynamic_weight_min_weight", Description = "The minimum weight the object can retract to."})
	cp:AddControl("Slider",  {Label = "Weight Change Per Second", Type = "Float", Min = 0.2, Max = 1000, Command = "dynamic_weight_change_rate", Description = "The rate at which the object's weight changes in units/second."})
	cp:AddControl("Checkbox", {Label = "Gravitate To Default", Command = "dynamic_weight_auto_return", Description = "Whether the object's weight returns to default when not increasing/decreasing."})
	cp:AddControl("Checkbox", {Label = "Keypad Toggles", Command = "dynamic_weight_toggle", Description = "Whether a single keypad press toggles on/off weight manipulation."})
	cp:AddControl("Checkbox", {Label = "Collide With Props", Command = "dynamic_weight_collide_objects", Description = "Whether the object collides with other props."})
	cp:AddControl("Checkbox", {Label = "Collide With World", Command = "dynamic_weight_collide_world", Description = "Whether the object collides with the world."})
end

function TOOL:UpdateGhostDynamicWeight (ent, player)
	if !ent then return end
	if !ent:IsValid() then return end

	local tr = utilx.GetPlayerTrace (player, player:GetCursorAimVector())
	local trace = util.TraceLine (tr)
	if !trace.Hit then return end

	if (trace.Entity && trace.Entity:GetClass() == "gmod_wire_npcspawnplatform" || trace.Entity:IsPlayer()) then
		ent:SetNoDraw (true)
		return
	end

	local Ang = trace.HitNormal:Angle()
	Ang.pitch = Ang.pitch + 90
	--Ang.roll = Ang.roll + 90

	local min = ent:OBBMins()
	ent:SetPos (trace.HitPos - trace.HitNormal * min.z)
	ent:SetAngles (Ang)

	ent:SetNoDraw (false)
end

function TOOL:Think()
	local model = self:GetClientInfo ("model")
	if !self.GhostEntity || !self.GhostEntity:IsValid() || self.GhostEntity:GetModel() != model then
		self:MakeGhostEntity (model, Vector(0,0,0), Angle(0,0,0))
	end

	self:UpdateGhostDynamicWeight (self.GhostEntity, self:GetOwner())
end
